﻿using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Drawing;
using System.IO;
using System.Threading;
using System.Web;
using System.Web.Caching;
using System.Web.Hosting;
using System.Web.Script.Serialization;
using Eksponent.CropUp.AutoDetect;
using Eksponent.CropUp.Config;
using System.Linq;

namespace Eksponent.CropUp
{
    public class DefaultCropDataProvider : ICropDataProvider
    {
        public IAutoDetector AutoDetector { get; set; }

        public bool UseCache { get; set; }

        private readonly string _dataDirectory;
        private ReaderWriterLockSlim _fileAccess = new ReaderWriterLockSlim();

        public DefaultCropDataProvider(string dataDirectory = "~/")
        {
            UseCache = true;
            if (dataDirectory.StartsWith("~"))
            {
                dataDirectory = HostingEnvironment.MapPath(dataDirectory);
            }
            _dataDirectory = dataDirectory;
        }


        /// <summary>
        /// Returns the JSON for the image's crop info at the virtual path.
        /// You can override this method to use, e.g., a blob storage instead
        /// </summary>
        /// <param name="virtualPath">The virtual path.</param>
        /// <returns></returns>
        public virtual string GetDataString(string virtualPath)
        {
            var path = GetDataPath(virtualPath);
            if (File.Exists(path))
            {
                _fileAccess.EnterReadLock();
                try
                {
                    return File.ReadAllText(path);
                }
                finally
                {
                    _fileAccess.ExitReadLock();
                }
            }
            
            return null;
        }

        /// <summary>
        /// Saves the JSON for the image's crop info at the virtual path.
        /// You can override this method to use, e.g., a blob storage instead
        /// </summary>
        /// <param name="virtualPath">The virtual path.</param>
        /// <param name="data">The data.</param>
        public virtual void SetDataString(string virtualPath, string data)
        {
            var path = GetDataPath(virtualPath);
            _fileAccess.EnterWriteLock();
            try
            {
                var f = new FileInfo(path);
                if (!f.Directory.Exists)
                {
                    f.Directory.Create();
                }
                File.WriteAllText(path, data);
            }
            finally
            {
                _fileAccess.ExitWriteLock();
            }
        }

        private const string Extension = ".crop.json";

        public virtual string GetDataPath(string virtualPath)
        {
            if (virtualPath.StartsWith("~/")) return HostingEnvironment.MapPath(virtualPath + Extension);
            if (virtualPath.StartsWith("/")) return HostingEnvironment.MapPath("~/" + virtualPath + Extension);
            
            return Path.Combine(_dataDirectory, virtualPath + Extension);
        }


        public ImageCropData GetCropData(string virtualPath)
        {
            virtualPath = VirtualPathUtility.ToAppRelative(virtualPath);
                        
            var data = GetCachedData(virtualPath);
            if (data != null) return data;

            var dataString = GetDataString(virtualPath);
            if (dataString != null)
            {
                var legacy = true;
                try
                {
                    var dict = JsonHelper.Serializer.Deserialize<Dictionary<string, object>>(dataString);
                    legacy = !((Dictionary<string, object>)dict["Default"]).ContainsKey("Relative");
                }
                catch { }

                data = JsonHelper.Serializer.Deserialize<ImageCropData>(dataString);
                bool changed = false;
                Size? size = null;

                if (!data.Default.Relative || legacy)
                {
                    size = CropUp.Dialect.GetImageSize(virtualPath);
                    data.Default = data.Default.ToRelative(size.Value.Width, size.Value.Height);
                    changed = true;
                }

                foreach (var alias in data.ManualCroppings.Keys.ToArray())
                {
                    var mc = data.ManualCroppings[alias];
                    if (!mc.Relative)
                    {
                        if (!size.HasValue) size = CropUp.Dialect.GetImageSize(virtualPath);
                        data.ManualCroppings[alias] = mc.ToRelative(size.Value.Width, size.Value.Height);
                        changed = true;
                    }
                }

                if (changed)
                {
                    SaveCropData(virtualPath, data);
                }

                return data;
            }

            if (AutoDetector != null)
            {
                var info = AutoDetector.SuggestCropInfo(virtualPath, CropUp.Dialect);
                if (info != null)
                {                                        
                    data = new ImageCropData { Default = info };
                    SaveCropData(virtualPath, data);
                    return data;
                }
            }

            return null;
        }

        public void SaveCropData(string virtualPath, ImageCropData cropData)
        {
            SetCachedData(virtualPath, cropData);
            SetDataString(virtualPath, JsonHelper.Serializer.Serialize(cropData));
        }

        protected virtual string GetCacheKey(string virtualPath)
        {
            if (virtualPath.StartsWith("~")) virtualPath = virtualPath.Substring(1);
            return "CropData_" + virtualPath.ToLower();
        }

        protected virtual ImageCropData GetCachedData(string virtualPath)
        {
            return UseCache ? (ImageCropData)HttpRuntime.Cache[GetCacheKey(virtualPath)] : null;
        }

        

        protected virtual void SetCachedData(string virtualPath, ImageCropData data)
        {
            if (!UseCache) return;


            HttpRuntime.Cache.Insert(GetCacheKey(virtualPath), data, null, Cache.NoAbsoluteExpiration, TimeSpan.FromMinutes(5));
        }
    }
}